package com.zwcl.payment.gateway.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.zwcl.common.core.config.RedisConfig;
import com.zwcl.common.core.exception.BusinessException;
import com.zwcl.common.core.redis.RedisLockLua;
import com.zwcl.common.core.utils.CollectionUtils;
import com.zwcl.common.core.utils.JsonUtils;
import com.zwcl.common.core.utils.SpringUtils;
import com.zwcl.payment.gateway.channel.WeixinMinaNormalService;
import com.zwcl.payment.gateway.constants.PayConstants;
import com.zwcl.payment.gateway.dto.PayParam;
import com.zwcl.payment.gateway.entity.PaymentLog;
import com.zwcl.payment.gateway.entity.PaymentMain;
import com.zwcl.payment.gateway.enums.PayStatusEnum;
import com.zwcl.payment.gateway.enums.PayWayEnum;
import com.zwcl.payment.gateway.enums.PaymentTypeEnum;
import com.zwcl.payment.gateway.mq.producer.MqProducer;
import com.zwcl.payment.gateway.service.PaymentAccountService;
import com.zwcl.payment.gateway.service.PaymentLogService;
import com.zwcl.payment.gateway.service.PaymentMainService;
import com.zwcl.payment.gateway.service.base.PayWayClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PaymentServiceImpl {

    @Autowired
    private RedisLockLua redisLock;

    @Autowired
    private PaymentMainService paymentMainService;

    @Autowired
    private PaymentLogService paymentLogService;

    @Autowired
    private PaymentAccountService paymentAccountService;

    @Autowired
    private MqProducer mqProducer;

    @Autowired
    private WeixinMinaNormalService weixinMinaNormalService;

    @Autowired
    private TransctionalServiceImpl transctionalService;

    /**
     * 发起支付
     * @param param
     * @return
     */
    public String payRequest(PayParam param) {
        log.info("开始发起支付，参数：{}", param);
        PaymentMain payment = null;

        //校验数据
        if(param.getMchType()==null){
            param.setMchType(paymentAccountService.getMchType(param.getOrderType(), param.getPayWay()));
        }
        List<PaymentMain> pList = paymentMainService.getPayNoByOrderNo(param.getOrderNo(), param.getOrderType());

        boolean lock = redisLock.lock(PayConstants.ZWCL_PAYMENT_LOCK_PREFIX+ param.getOrderNo(), param.getUserId().toString(), PayConstants.ZWCL_PAY_EXPIRE_TIME, RedisConfig.DEFAULT_TRY_LOCK_TIMEOUT);
        if (!lock) {
            log.error("发起主动支付拦截并发入库，参数：{}", JsonUtils.objectToJson(param));
            throw new BusinessException("请求频率过高或订单正尝试重试扣费中，请稍后重试");
        }
        try {
            //订单校验
            if (CollectionUtils.isNotEmpty(pList)) {
                //判断是否已支付过(以下这个判断逻辑是否有点问题)
                long success = pList.stream().filter(a -> a.getPayStatus().equals(PayStatusEnum.PAID.getValue()) ||
                        (a.getPaymentType().equals(PaymentTypeEnum.AUTO.getValue()) &&
                                (a.getPayStatus().equals(PayStatusEnum.UNPAID.getValue()) || a.getPayStatus().equals(PayStatusEnum.PAYING.getValue())))
                ).count();
                if (success > 0) {
                    throw new BusinessException("该订单已支付成功，不能重复支付");
                }
                //用同一笔支付流水发起，避免微信回调异常或回调慢导致的重复支付
                if (param.getPayWay().equals(PayWayEnum.WECHAT_MINA.getValue())
                        || param.getPayWay().equals(PayWayEnum.WECHAT_MINA_NORMAL.getValue())) {
                    List<PaymentMain> unPaidList = pList.stream().filter(a -> a.getPayStatus().equals(PayStatusEnum.UNPAID.getValue())).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(unPaidList)) {
                        payment = unPaidList.get(0);
                        if (payment.getPayAmount().compareTo(param.getPayAmount()) != 0 ||
                                !payment.getOpenId().equals(param.getOpenId())) {
                            //可以加入邮件通知
                            //warnService.warn("发起小程序支付异常，订单号重复：" + payment.getPaymentNo(), JSON.toJSONString(param));
                            throw new BusinessException("订单用户或金额发生变动，请稍后再试");
                        }
                    }
                }
            }

            //小范围事务保持一致，获取预支付信息
            return transctionalService.getPayInfo(payment,param);

//            //发起扣费(上面方法抛出异常，则不会继续往下执行)
//            PayWayClient client = SpringUtils.getBean("PayWayClient_" + param.getPayWay());
//            String payInfo = client.pay(payment);
//
//            //加入取消订单队列，9就是延时5分钟
//            mqProducer.cancelOrder(payment.getPaymentNo(), 9);
//            return payInfo;
        }catch (Exception ex){
            log.error("发起预支付失败：{}",ex);
            throw new BusinessException("发起预支付失败。");
        }finally {
            redisLock.unlock(PayConstants.ZWCL_PAYMENT_LOCK_PREFIX+ param.getOrderNo(), param.getUserId().toString());
        }
    }

    /**
     * 取消支付订单
     * @param paymentNo
     */
    public void cancelOrder(String paymentNo) {
        PaymentMain payment = paymentMainService.getByNo(paymentNo);
        if(payment==null){
            log.error("取消订单错误，原因：原支付流水不存在，paymentNo={}", paymentNo);
            return;
        }
        if(payment.getPayStatus().equals(PayStatusEnum.UNPAID.getValue())){
            try {
                //调用微信的关单，有可能在这个时间点，用户已经支付成功了，此时调用微信接口关单则会报错
                if(payment.getPayWay().equals(PayWayEnum.WECHAT_MINA_NORMAL.getValue())){
                    weixinMinaNormalService.closeOrder(paymentNo, payment.getMchType());
                } else if(payment.getPayWay().equals(PayWayEnum.WECHAT_MINA.getValue())){
                    //TODO:以后可以扩展微信服务商的关单
                } else{
                    return;
                }
                //将订单状态更新为已取消
                paymentMainService.update(
                        new PaymentMain().setPayStatus(PayStatusEnum.CANCEL.getValue()),
                        new UpdateWrapper<PaymentMain>().lambda().eq(PaymentMain::getPaymentNo, paymentNo)
                );

                //添加日志
                paymentLogService.addLog(
                        new PaymentLog().setPaymentNo(paymentNo).setPayStatus(PayStatusEnum.CANCEL.getValue()).setOperationInfo(JSON.toJSONString(new PaymentMain().setPaymentNo(paymentNo)))
                );
            } catch (Exception e) {
                //微信关单失败抛出异常，有可能此时用户已经支付成功了，因此主动查询微信状态，修改订单为已支付
                log.error("发起微信关闭订单接口出错，paymentNo={}", paymentNo, e);
                PayWayClient client = SpringUtils.getBean("PayWayClient_" + payment.getPayWay());
                client.payQueryHandle(payment);
            }
        }
    }

}
